package com.jxpowernode.bitcon.service.btcadder;

import com.jxpowernode.bitcon.utils.Hash;
import org.bitcoinj.core.Base58;

import java.security.*;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECPoint;
import java.util.Arrays;

/**
 * @Author: Lixuewei
 * @Email:1410511702@qq.com
 * @Desc: 该类用于推导生成原始格式的比特币地址
 * @Date: 2021/12/20 14:28
 */
public class Address {

    /**
     * 用于创建比特币地址并返回地址
     * @return 返回的到的string类型地址
     */
    public static String bitconAdder(){
        Address address = new Address();
        KeyPair keyPair = address.generateKeys();
        return address.newAddress(keyPair);
    }

    /**
     * 该方法用于生成一对随机的密钥对
     * @return 生成的密钥对
     */
    public KeyPair generateKeys(){
        try {
            //生成密钥
            KeyPairGenerator generator =KeyPairGenerator.getInstance("EC");
            //参数类型
            ECGenParameterSpec spec = new ECGenParameterSpec("secp256k1");
            //初始化
            generator.initialize(spec);
            //将生成的密钥对返回
            return  generator.generateKeyPair();
        } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 该方法用于根据密钥给定的密钥数据，推导生成对应的原始格式的比特币地址
     * @param keyPair 生成的密钥对
     * @return  生成的比特币地址
     */
    public String newAddress(KeyPair keyPair){
        PublicKey aPublic = keyPair.getPublic();
        ECPublicKey epub = (ECPublicKey) aPublic;
        ECPoint point = epub.getW();
        byte[] pubKey = toUncompressPubKey(point);
        //第一次对公钥进行hash计算
        byte[] pubKeyHash256 = Hash.SHA256(pubKey);
        //第二次进行hah计算
        byte[] pubHash = Hash.RipeMD160(pubKeyHash256);
        byte[] version = new byte[1];
        version[0] = 0x00;//版本号
        byte[] var_pubHash = new byte[version.length+pubHash.length];
        System.arraycopy(version,0,var_pubHash,0,version.length);
        System.arraycopy(pubHash,0,var_pubHash,1,pubHash.length);
        //双haah取前四个字节
        byte[] hash1 = Hash.SHA256(var_pubHash);
        byte[] hash2 = Hash.SHA256(hash1);
        byte[] check = new byte[4];
        System.arraycopy(hash2,0,check,0,check.length);
        byte[] ver_pubhash_check = new byte[var_pubHash.length+check.length];
        System.arraycopy(var_pubHash,0,ver_pubhash_check,0,var_pubHash.length);
        System.arraycopy(check,0,ver_pubhash_check,var_pubHash.length,check.length);
        //对拼接到的字节数组进行base58编码
        String data = Base58.encode(ver_pubhash_check);

        return data ;
    }

    /**
     * 该方法用于生成非压缩格式的公钥数据，并返回
     * @return 非压缩格式的公钥
     */
    public static  byte[] toUncompressPubKey(ECPoint point){
        byte[] xBytes = point.getAffineX().toByteArray();
        byte[] yBytes = point.getAffineY().toByteArray();
        //原始格式：04+x+y
        byte[] pubkey = new byte[1+2*32];
        pubkey[0] = 04;
        if(xBytes.length == 32){
            //通过拷贝完成拼接
            System.arraycopy(xBytes,0,pubkey,1,xBytes.length);
        }else if(xBytes.length > 32){
            //只取后32字节
            System.arraycopy(xBytes,1,pubkey,1,32);
        }
        if(yBytes.length == 32){
            //通过拷贝完成拼接
            System.arraycopy(yBytes,0,pubkey,33,yBytes.length);
        }else if(yBytes.length > 32){
            //只取后32字节
            System.arraycopy(yBytes,1,pubkey,33,32);
        }
        return pubkey;
    }

    /**
     * 校验比特币地址是否符合规范，并返回结果
     * @param addr 要校验的字符串
     * @return 返回校验的结果
     */
    public static boolean validateAdder(String addr){
        if(addr == null || addr.equals("")){
            return false;
        }
        byte[] ver_pubhash_check = Base58.decode(addr);
        if(ver_pubhash_check.length<4){
            return false;
        }
        //截断校验位
        byte[] check = new byte[4];
        System.arraycopy(ver_pubhash_check,ver_pubhash_check.length-4,check,0,check.length);
        byte[] ver_pubhash = new byte[ver_pubhash_check.length-4];
        System.arraycopy(ver_pubhash_check,0,ver_pubhash,0,ver_pubhash.length);
        //双hash取前四个字节
        byte[] hash1 = Hash.SHA256(ver_pubhash);
        byte[] hash2 = Hash.SHA256(hash1);
        byte[] check_sum = new byte[4];
        System.arraycopy(hash2,0,check_sum,0,check_sum.length);
        //比较两个字节数组是否相等
        return   Arrays.equals(check,check_sum);
    }
}
